package com.innovation.ic.im.end.web.controller.im_erp9;

import com.google.common.base.Strings;
import com.innovation.ic.b1b.framework.util.StringUtils;
import com.innovation.ic.im.end.base.model.im_erp9.Message;
import com.innovation.ic.im.end.base.pojo.ApiResult;
import com.innovation.ic.im.end.base.pojo.ServiceResult;
import com.innovation.ic.im.end.base.pojo.constant.MessageType;
import com.innovation.ic.im.end.base.pojo.constant.RabbitMqExchangeMap;
import com.innovation.ic.im.end.base.pojo.enums.AvailableStatusEnum;
import com.innovation.ic.im.end.base.pojo.enums.ReadStatusEnum;
import com.innovation.ic.im.end.base.pojo.im_erp9.RetractionPojo;
import com.innovation.ic.im.end.base.service.im_erp9.ClientService;
import com.innovation.ic.im.end.base.thread.web.SendRabbitMqMessageThread;
import com.innovation.ic.im.end.base.thread.web.SendRabbitMqReadMsgThread;
import com.innovation.ic.im.end.base.thread.web.SendWithdrawRabbitMqMessageThread;
import com.innovation.ic.im.end.base.value.config.RabbitMqParamConfig;
import com.innovation.ic.im.end.base.value.config.RedisParamConfig;
import com.innovation.ic.im.end.base.vo.im_erp9.MessageVo;
import com.innovation.ic.im.end.web.controller.AbstractController;
import com.innovation.ic.im.end.web.endpoint.Endpoint;
import com.innovation.ic.im.end.web.thread.*;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.ParseException;
import java.util.*;

/**
 * 消息API
 */
@Api(value = "消息API", tags = "MessageController")
@RestController
@RequestMapping("/api/v1/message")
@DefaultProperties(defaultFallback = "defaultFallback")
public class MessageController extends AbstractController {
    private static final Logger log = LoggerFactory.getLogger(MessageController.class);

    @Resource
    private ClientService clientService;

    @Resource
    private RedisParamConfig redisParamConfig;

    @Resource
    private RabbitMqParamConfig rabbitMqParamConfig;

    /**
     * 根据参数type，获取用户的聊天记录，并按照创建时间降序排列，支持分页显示，过滤掉不可见的记录
     * @return
     */
    @HystrixCommand
    @ApiOperation(value = "根据参数type，获取用户的聊天记录，并按照创建时间降序排列，支持分页显示，过滤掉不可见的记录")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fromUserAccount", value = "发送消息的用户的账号", required = true, dataType = "String"),
            @ApiImplicitParam(name = "toUserAccount", value = "接收消息的用户的账号", required = true, dataType = "String"),
            @ApiImplicitParam(name = "pageNo", value = "第几页", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "pageSize", value = "每页行数", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "type", value = "0表示全部，2表示文件，3表示图片", required = true, dataType = "Integer")
    })
    @RequestMapping(value = "/pageByType/{fromUserAccount}/{toUserAccount}/{pageSize}/{pageNo}/{type}", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"})
    @ResponseBody
    public ResponseEntity<ApiResult> pageByType(@PathVariable("fromUserAccount") String fromUserAccount,
                                                               @PathVariable("toUserAccount") String toUserAccount,
                                                               @PathVariable("pageSize") Integer pageSize,
                                                               @PathVariable("pageNo") Integer pageNo,
                                                               @PathVariable("type") Integer type,
                                                               HttpServletRequest request, HttpServletResponse response) {
        if (!StringUtils.validateParameter(fromUserAccount) || !StringUtils.validateParameter(toUserAccount)
                || null == pageSize || null == pageNo || null == type) {
            String message = "调用接口【/api/v1/message/pageByType/{fromUserAccount}/{toUserAccount}/{pageSize}/{pageNo}/{type}时，" +
                    "参数fromUserAccount、toUserAccount、pageSize、pageNo、type不能为空";
            log.warn(message);
            ApiResult<List<Message>> apiResult = new ApiResult<>();
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(HttpStatus.BAD_REQUEST.value());
            apiResult.setMessage(message);
            return new ResponseEntity<>(apiResult, HttpStatus.BAD_REQUEST);
        }

        ServiceResult<List<Message>> messageListServiceResult = messageService.redisPage(fromUserAccount, toUserAccount, pageSize, pageNo, type, redisParamConfig.getTimeout());

        ApiResult<List<Message>> apiResult = new ApiResult<>();
        apiResult.setSuccess(Boolean.TRUE);
        apiResult.setCode(HttpStatus.OK.value());
        apiResult.setResult(messageListServiceResult.getResult());
        apiResult.setMessage(ServiceResult.SELECT_SUCCESS);
        return new ResponseEntity<>(apiResult, HttpStatus.OK);
    }

    /**
     * 根据参数type和content（模糊查询），获取用户的聊天记录，并按照创建时间降序排列，支持分页显示，过滤掉不可见的记录
     * @return
     */
    @HystrixCommand
    @ApiOperation(value = "根据参数type和content（模糊查询），获取用户的聊天记录，并按照创建时间降序排列，支持分页显示，过滤掉不可见的记录")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "messageVo", value = "Message的vo类", required = true, dataType = "MessageVo"),
            @ApiImplicitParam(name = "fromUserAccount", value = "发送消息的用户的账号", required = true, dataType = "String"),
            @ApiImplicitParam(name = "toUserAccount", value = "接收消息的用户的账号", required = true, dataType = "String"),
            @ApiImplicitParam(name = "pageNo", value = "第几页", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "pageSize", value = "每页行数", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "type", value = "0表示全部，2表示文件", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "content", value = "内容", required = true, dataType = "Integer")
    })
    @RequestMapping(value = "/searchAndPageByType", method = RequestMethod.POST, produces = {"application/json; charset=utf-8"})
    @ResponseBody
    public ResponseEntity<ApiResult> searchAndPageByType(@RequestBody MessageVo messageVo,
                                                                        HttpServletRequest request, HttpServletResponse response) {
        if (!StringUtils.validateParameter(messageVo.getFromUserAccount())
                || !StringUtils.validateParameter(messageVo.getToUserAccount())
                || messageVo.getPageNo() == null || messageVo.getPageSize() == null ||
                messageVo.getType() == null) {

            String message = "调用接口【/api/v1/message/searchAndPageByType时，" +
                    "参数fromUserAccount、toUserAccount、pageSize、pageNo、type不能为空";
            log.warn(message);
            ApiResult<List<Message>> apiResult = new ApiResult<>();
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(HttpStatus.BAD_REQUEST.value());
            apiResult.setMessage(message);
            return new ResponseEntity<>(apiResult, HttpStatus.BAD_REQUEST);
        }
        ServiceResult<List<Message>> messageListServiceResult = messageService.redisLikePage(messageVo, redisParamConfig.getTimeout());

        ApiResult<List<Message>> apiResult = new ApiResult<>();
        apiResult.setSuccess(Boolean.TRUE);
        apiResult.setCode(HttpStatus.OK.value());
        apiResult.setResult(messageListServiceResult.getResult());
        apiResult.setMessage(ServiceResult.SELECT_SUCCESS);
        return new ResponseEntity<>(apiResult, HttpStatus.OK);
    }

    /**
     * 根据参数fromUserAccount、toUserAccount、initRow和direction，获取用户的某个聊天记录的上下文记录，并按照创建时间降序排列，分别支持向上和向下分页显示，过滤掉不可见的记录
     * @return
     */
    @HystrixCommand
    @ApiOperation(value = "根据参数messageId、initRow和direction，获取用户的某个聊天记录的上下文记录，并按照创建时间降序排列，分别支持向上和向下分页显示，过滤掉不可见的记录")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fromUserAccount", value = "发送消息的用户的账号", required = true, dataType = "String"),
            @ApiImplicitParam(name = "toUserAccount", value = "接收消息的用户的账号", required = true, dataType = "String"),
            @ApiImplicitParam(name = "id", value = "message表的id", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "initRow", value = "第一次显示时，当前记录上面的记录数或下面的记录数", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "direction", value = "分页显示的方向。1表示向下，2表示向上", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "pageSize", value = "每页行数", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "pageNo", value = "第几页", required = true, dataType = "Integer")

    })
    @RequestMapping(value = "/context/{fromUserAccount}/{toUserAccount}/{id}/{initRow}/{direction}/{pageSize}/{pageNo}", method = RequestMethod.POST, produces = {"application/json; charset=utf-8"})
    @ResponseBody
    public ResponseEntity<ApiResult> context(@PathVariable("fromUserAccount") String fromUserAccount,
                                                            @PathVariable("toUserAccount") String toUserAccount,
                                                            @PathVariable("id") Integer id,
                                                            @PathVariable("initRow") Integer initRow,
                                                            @PathVariable("direction") Integer direction,
                                                            @PathVariable("pageSize") Integer pageSize,
                                                            @PathVariable("pageNo") Integer pageNo,
                                                            HttpServletRequest request, HttpServletResponse response) {
        if (!StringUtils.validateParameter(fromUserAccount) || !StringUtils.validateParameter(toUserAccount)
                || null == pageSize || null == pageNo || null == initRow || null == id || null == direction) {
            String message = "调用接口【/api/v1/message/context/{fromUserAccount}/{toUserAccount}/{initRow}/{direction}/{pageSize}/{pageNo}时，" +
                    "参数fromUserAccount、toUserAccount、id、initRow、direction、pageSize、pageNo不能为空";
            log.warn(message);
            ApiResult<List<Message>> apiResult = new ApiResult<>();
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(HttpStatus.BAD_REQUEST.value());
            apiResult.setMessage(message);
            return new ResponseEntity<>(apiResult, HttpStatus.BAD_REQUEST);
        }

        ServiceResult<List<Message>> messageListServiceResult = messageService.redisContextPage(fromUserAccount,toUserAccount,id,initRow,direction,pageSize,pageNo,redisParamConfig.getTimeout());

        ApiResult<List<Message>> apiResult = new ApiResult<>();
        apiResult.setSuccess(Boolean.TRUE);
        apiResult.setCode(HttpStatus.OK.value());
        apiResult.setResult(messageListServiceResult.getResult());
        apiResult.setMessage(ServiceResult.SELECT_SUCCESS);
        return new ResponseEntity<>(apiResult, HttpStatus.OK);
    }

    /**
     * 恢复未读消息，先在message表中通过fromUserAccount对toUserAccount修改消息状态，toUserAccount发送到消息服务器上。
     * @return
     */
    @HystrixCommand
    @ApiOperation(value = "先在message表中通过fromUserAccount对toUserAccount修改消息状态，toUserAccount发送到消息服务器上")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fromUserAccount", value = "发送消息的用户账号(对方)", required = true, dataType = "String"),
            @ApiImplicitParam(name = "toUserAccount", value = "接收消息的用户账号(自己)", required = true, dataType = "String")
    })
    @RequestMapping(value = "/restore/{fromUserAccount}/{toUserAccount}", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"})
    @ResponseBody
    public ResponseEntity<ApiResult> restore(@PathVariable("fromUserAccount") String fromUserAccount,
                                             @PathVariable("toUserAccount") String toUserAccount,
                                             HttpServletRequest request, HttpServletResponse response) {
        if (!StringUtils.validateParameter(fromUserAccount) || !StringUtils.validateParameter(toUserAccount)) {
            String message = "调用接口【/api/v1/Message/restore/{fromUserAccount}/{toUserAccount}时，" +
                    "参数fromUserAccount和toUserAccount不能为空";
            log.warn(message);
            ApiResult<Integer> apiResult = new ApiResult<>();
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(HttpStatus.BAD_REQUEST.value());
            apiResult.setMessage(message);
            return new ResponseEntity<>(apiResult, HttpStatus.BAD_REQUEST);
        }
        // 查询未读消息id集合
        ServiceResult<List<String>> unreadMsgResult = messageService.getUnreadMsgList(fromUserAccount, toUserAccount);

        // 恢复未读消息
        ServiceResult<Integer> result = messageService.restoreMessage(fromUserAccount, toUserAccount);

        // 推送rabbitMq消息
        if(result != null && result.getResult() != null && result.getResult() > 0){
            // 将本次处理成功的未读消息id通过mq推送给前端
            SendRabbitMqReadMsgThread sendRabbitMqReadMsgThread = new SendRabbitMqReadMsgThread(fromUserAccount,
                    rabbitMqParamConfig.getExchange().get(RabbitMqExchangeMap.READ_STATE_UPDATE_MSG_EXCHANGE), unreadMsgResult.getResult());
            threadPoolManager.execute(sendRabbitMqReadMsgThread);
        }

        // 给接收方推送消息，更新最近联系人列表
        List<String> list = new ArrayList<>();
        list.add(toUserAccount);
        SendRabbitMqMessageThread sendRabbitMqMessageThread = new SendRabbitMqMessageThread(list, rabbitMqParamConfig.getExchange().get(RabbitMqExchangeMap.CURRENT_EXCHANGE));
        threadPoolManager.execute(sendRabbitMqMessageThread);

        ApiResult<Integer> apiResult = new ApiResult<>();
        apiResult.setSuccess(Boolean.TRUE);
        apiResult.setCode(HttpStatus.OK.value());
        apiResult.setMessage(ServiceResult.UPDATE_SUCCESS);
        apiResult.setResult(result.getResult());
        return new ResponseEntity<>(apiResult, HttpStatus.OK);
    }

    /**
     * 一对一聊天，撤回消息
     * @return 返回处理结果
     */
    @HystrixCommand
    @ApiOperation(value = "一对一聊天，撤回消息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "消息的id", required = true, dataType = "Integer"),
            @ApiImplicitParam(name = "time", value = "撤销时间（时间戳，精确到秒）", required = true, dataType = "Integer")
    })
    @RequestMapping(value = "/retraction/{id}/{dateTime}", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"})
    @ResponseBody
    public ResponseEntity<ApiResult> retraction(@PathVariable("id") Integer id,
                                                @PathVariable("dateTime") Integer dateTime,
                                                HttpServletRequest request, HttpServletResponse response) throws ParseException {
        if (null == id || null == dateTime) {
            String message = "调用接口【/api/v1/Message/retraction/{id}/{dateTime}时，" +
                    "参数id和dateTime不能为空";
            log.warn(message);
            ApiResult<Boolean> apiResult = new ApiResult<>();
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(HttpStatus.BAD_REQUEST.value());
            apiResult.setMessage(message);
            return new ResponseEntity<>(apiResult, HttpStatus.BAD_REQUEST);
        }
        RetractionPojo retractionPojo = messageService.retractionMessage(id, dateTime);

        // 消息撤回成功给接收消息的人推送mq消息并发送通知消息
        if(retractionPojo.getMessage() != null){
            // 给接收消息的人推送mq消息
            List<String> refGroupAccountList = new ArrayList<>();
            refGroupAccountList.add(retractionPojo.getMessage().getToUserAccount());
            SendWithdrawRabbitMqMessageThread sendWithdrawRabbitMqMessageThread = new SendWithdrawRabbitMqMessageThread(refGroupAccountList, retractionPojo, rabbitMqParamConfig.getExchange().get(RabbitMqExchangeMap.RETRACTION_EXCHANGE));
            threadPoolManager.execute(sendWithdrawRabbitMqMessageThread);

            // 给接收消息的人发送通知消息
            saveRetractionMsgAndSendToUserAccount(retractionPojo.getMessage());
        }

        ApiResult<Boolean> apiResult = new ApiResult<>();
        apiResult.setSuccess(Boolean.TRUE);
        apiResult.setCode(HttpStatus.OK.value());
        apiResult.setMessage(ServiceResult.UPDATE_SUCCESS);
        return new ResponseEntity<>(apiResult, HttpStatus.OK);
    }

    /**
     * 给接收消息的人发送通知消息
     * @param retractionMessage 撤回消息内容
     */
    private void saveRetractionMsgAndSendToUserAccount(Message retractionMessage) {
        Map<String, Endpoint> onlineMap = Endpoint.onlineMap;

        // 保存通知消息
        Message message = new Message();
        message.setFromUserAccount(retractionMessage.getFromUserAccount());
        message.setToUserAccount(retractionMessage.getToUserAccount());
        if(!Strings.isNullOrEmpty(retractionMessage.getFromUserRealName())){
            message.setContent(retractionMessage.getFromUserRealName() + "撤回了一条消息");
        }else{
            message.setContent(retractionMessage.getFromUserAccount() + "撤回了一条消息");
        }
        message.setType(MessageType.SYS_MSG);
        message.setFromUserRealName(retractionMessage.getFromUserRealName());
        message.setToUserRealName(retractionMessage.getToUserRealName());
        message.setRead(ReadStatusEnum.READ.getCode());
        message.setReadTime(new Date(System.currentTimeMillis()));
        message.setFromUserAvailable(AvailableStatusEnum.AVAILABLE.getCode());
        message.setToUserAvailable(AvailableStatusEnum.AVAILABLE.getCode());
        message.setRetractionContent(retractionMessage.getContent());
        message.setCreateTime(retractionMessage.getCreateTime());
        message.setRetractionMsgId(retractionMessage.getId());
        SaveRetractionMessageThread saveRetractionMessageThread = new SaveRetractionMessageThread(message, onlineMap,
                rabbitMqParamConfig.getExchange().get(RabbitMqExchangeMap.CURRENT_EXCHANGE), clientService.getLoginTypeList().getResult(), threadPoolManager);
        threadPoolManager.execute(saveRetractionMessageThread);
    }
}